package visualization;

import java.awt.Color;
import java.util.ArrayList;
import java.util.List;

import javax.media.j3d.Appearance;
import javax.media.j3d.BranchGroup;
import javax.media.j3d.ColoringAttributes;
import javax.media.j3d.GeometryArray;
import javax.media.j3d.LineArray;
import javax.media.j3d.LineAttributes;
import javax.media.j3d.Node;
import javax.media.j3d.Shape3D;
import javax.vecmath.Color3f;

import neuron.Coord;
import neuron.Dendrite;
import neuron.Layers;
import neuron.Neuron;
import neuron.Segment;

import com.sun.j3d.utils.geometry.Sphere;

/**
 * Convert neuronal structures to J3D objects.
 * 
 * @author risto
 *
 */
public class NeuronJ3D {

	public static double termSegLenThreshold = 10;
	
	public static LineArray segmentToLineArray(Segment seg)
	{
		// get segment coordinates
		List<Coord> coords = seg.getPoints();
		int size = coords.size();

		// get starting point (from previous segment)
		Coord start = seg.getStartCoordinate();
		if (start != null) size++;

		// create line array
//		System.out.println("Line size " + size);
        LineArray la = new LineArray(size*2, GeometryArray.COORDINATES);
        int i = 0;

        double[] prev = null;
        if (start != null) prev = start.asArray();
        
        // add coordinates
        for (Coord c : coords) {
        	double[] cur = c.asArray();
        	
        	if (prev != null) {
            	la.setCoordinate(i++, prev);
            	la.setCoordinate(i++, cur);
        	}
        	prev = cur;
        }

        return la;
	}
	
	public static Shape3D segmentAsLine(Segment seg)
	{
		return segmentAsLine(seg, Color.black, 1.5f);
	}

	public static Shape3D segmentAsLine(Segment seg, Color color)
	{
		return segmentAsLine(seg, color, 1.5f);
	}

	public static Shape3D segmentAsLine(Segment seg, Color color, float lineWidth)
	{
        Shape3D line = new Shape3D(segmentToLineArray(seg));
        Appearance app = new Appearance();
        app.setColoringAttributes(new ColoringAttributes(new Color3f(color), ColoringAttributes.SHADE_FLAT));
        app.setLineAttributes(new LineAttributes(lineWidth, LineAttributes.PATTERN_SOLID, true));
        line.setAppearance(app);
        return line;
	}

	public static void addDendriteToGroup(Dendrite d, BranchGroup group)
	{
		addDendriteToGroup(d, group, Color.black, 1.5f);
	}
	
	 
	public static void addDendriteToGroup(Dendrite d, BranchGroup group, Color color, float lw)
	{
		List<Segment> segs = new ArrayList<Segment>();
		d.getRootSegment().collectSegments(segs);
		// Add all segments to group as lines
		for (Segment s : segs) {
			group.addChild(segmentAsLine(s, color, lw));
		}
	}

	public static Node getSoma(Neuron n)
	{
		// create gray sphere to approximate soma, for now..
        Appearance app = new Appearance();
        app.setColoringAttributes(new ColoringAttributes(new Color3f(Color.gray), ColoringAttributes.SHADE_FLAT));
        Sphere soma = new Sphere(10f);
        soma.setAppearance(app);
        return soma;
	}
	
	public static BranchGroup neuronToGroup(Neuron n)
	{
		return neuronToGroup(n, Colormap.FIXED);
	}

	private static void colorShortSegments(BranchGroup group, List<Dendrite> dendList)
	{
		for (Dendrite d : dendList) {
			List<Segment> lst = d.getTerminalSegments();
	//		System.out.println("Dendrite " + d + " has " + lst.size() + " terminal segments");
			for (Segment s : lst) {
//				System.out.println(s.length() + " < " + termSegLenThreshold + "?");
				if (s.length() < termSegLenThreshold) {
					System.out.println("S " + s.length() + " < " + termSegLenThreshold);
					group.addChild(segmentAsLine(s, Color.red.brighter().brighter(), 5f));
				}
			}
		}
		
	}
	
	public static BranchGroup neuronToGroup(Neuron n, Colormap map)
	{
		BranchGroup group = new BranchGroup();
        group.setCapability(BranchGroup.ALLOW_DETACH);

 		/*
		// check short terminals
		for (Dendrite d : n.basalDendrites()) {
			List<Segment> lst = d.getTerminalSegments();
	//		System.out.println("Dendrite " + d + " has " + lst.size() + " terminal segments");
			for (Segment s : lst) {
//				System.out.println(s.length() + " < " + termSegLenThreshold + "?");
				if (s.length() < termSegLenThreshold) {
					group.addChild(segmentAsLine(s, Color.red.brighter().brighter(), 5f));
				}
			}
		}
*/
		// add basal dendrites
		for (Dendrite d : n.basalDendrites()) addDendriteToGroup(d, group, map.getColor(0), 1.5f);
		
		// add apical dendrite
		if (n.getApicalDendrite() != null) {

			// Paint apical base with green
			List<Segment> apicalBase = n.getApicalDendrite().getApicalBasePath();
			for (Segment s : apicalBase) {
				group.addChild(segmentAsLine(s,  map.getColor(1), 3f));
			}
			
			// Paint obliques with blue
			List<Segment> obliques = n.getApicalDendrite().getObliqueRoots();
			for (Segment obRoot : obliques) {
				List<Segment> path = new ArrayList<Segment>();
				obRoot.collectSegments(path);
				for (Segment s : path) {
					group.addChild(segmentAsLine(s,  map.getColor(2), 1.5f));
				}
			}
 
			List<Segment> tuft2 = n.getApicalDendrite().getTuftRoot();
			if (tuft2 != null) {
				for (Segment r : tuft2) {
					List<Segment> path = new ArrayList<Segment>();
					r.collectSegments(path);
					for (Segment s : path) {
						group.addChild(segmentAsLine(s,  map.getColor(3), 1.5f));
					}
				}
			}
			

			// base apical with red 
//			addDendriteToGroup(n.getApicalDendrite(), group, Color.red.darker().darker(), 1f);

		}

		// add axon
		Dendrite axon = n.getAxon();
		if (axon != null) {
			addDendriteToGroup(axon, group, map.getColor(4), 2f);
		}
	
		colorShortSegments(group, n.basalDendrites());
		colorShortSegments(group, n.getApicalDendrite().getObliqueDendrites());
		colorShortSegments(group, n.getApicalDendrite().getTuftDendriteAsList());

		// add soma
		group.addChild(getSoma(n));
		group.compile();

		return group;
	}
}
